package com.shhic.hhplt.server.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.UpdateOptions;
import com.shhic.hhplt.server.exceptions.RequestParamException;
import com.shhic.hhplt.server.mapper.ITestReportDao;
import com.shhic.hhplt.server.model.*;
import com.shhic.hhplt.server.service.ITestReportService;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

@Service
@DS("hhpltTest")
@Slf4j
public class TestReportServiceImpl implements ITestReportService {
    @Autowired
    private ITestReportDao trDao;
    @Autowired
    private MongoCollection<Document> collection;

    @Autowired
    @Qualifier("backup")
    private MongoCollection<Document> backupColl;

    public void keepProductBindingCodeUniformity() {
        while(true){
            ProductBindingCode ori = null;
            try{
                ori = trDao.queryUniformityNonEqualsYes();
                if(ori!=null){
                    List<ProductBindingCode> pbcs = trDao.queryByProductNameAndIdCode(ori.getProductName(), ori.getIdCode());
                    adjustData(pbcs);
                }else{
                    Thread.sleep(5*60*1000);
                }
            }catch (Exception e){
              if(ori!=null){
                  log.error("保持数据一致性时发生异常---->{}",e.getMessage());
                  log.error("错误提示信息---->idCode={}",ori.getIdCode());
              }
                try {
                    Thread.sleep(60*1000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 数据不一致调整，以mysql数据表为主
     * */
    @Transactional(rollbackFor = Exception.class)
    public void adjustData(List<ProductBindingCode> pbcs){
        for (ProductBindingCode pbc : pbcs) {
            FindIterable<Document> documents = collection.find(
                    Filters.and(
                            Filters.eq(MongoConstantName.productName,pbc.getProductName()),
                            Filters.eq(MongoConstantName.idCode,pbc.getIdCode()),
                            Filters.eq(pbc.getCodeName(),pbc.getCode())
                    )
            );
            if(documents.first()==null){
                Document search=new Document();
                search.append(MongoConstantName.productName,pbc.getProductName());
                search.append(MongoConstantName.idCode,pbc.getIdCode());
                Document update=new Document();
                update.append(pbc.getCodeName(),pbc.getCode());
                if(pbc.getBindingTime()!=0){
                    update.append(MongoConstantName.createTime,new Date(pbc.getBindingTime()));
                }
                update.append(MongoConstantName.updateTime,new Date());
                Document updateSet=new Document();
                updateSet.append("$set",update);
                collection.updateOne(search,updateSet, new UpdateOptions().upsert(true));
            }
            trDao.setYesForUniformity(pbc);
        }
    }


    @Override
    public ProductTestResult getProductTestResult(String productName, String idCode) {
        return trDao.getProductTestResult(productName, idCode);
    }

    @Override
    public List<ProductSuite> getProductTestStatusList(String productName,
                                                       String idCode) {
        return trDao.getProductTestStatusList(productName, idCode);
    }

    @Override
    public void insertProductTestResult(ProductTestResult p) {
        trDao.insertProductTestResult(p);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertProductSuite(ProductTestResult p,
                                   List<ProductSuite> ptsList) {
        for (ProductSuite ps : ptsList) {
            trDao.deleteProductSuite(p.getProductName(), p.getIdCode(), ps.getSuiteName());
            trDao.insertProductSuite(ps);
            if (ps.getResult() == TestResultEnum.FAIL) {
                p.setResult(TestResultEnum.FAIL);
                trDao.updateProductTestResult(p);
            }
        }
    }

    @Override
    public void updateProductTestResultSucc(String productName, String idCode) {
        ProductTestResult p=new ProductTestResult();
        p.setResult(TestResultEnum.PASS);
        p.setIdCode(idCode);
        p.setProductName(productName);
        trDao.updateProductTestResult(p);
    }

    @Override
    public String getTestReportXml(String persistLocation) {
        return trDao.getTestReportXml(persistLocation);
    }

    @Override
    public void insertTestReportXml(String persistLocation, String reportXmlStr) {
        trDao.insertTestReportXml(persistLocation,reportXmlStr);
    }

    @Override
    public void updateTestReportXml(String persistLocation, String reportXmlStr) {
        trDao.updateTestReportXml(persistLocation,reportXmlStr);
    }

    @Override
    public List<ProductSuite> queryProductSuite(ProductSuiteQuery query) {
        return trDao.queryProductSuite(query);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertProductBindingCode(List<ProductBindingCode> pbcl) {
        for (ProductBindingCode pbc : pbcl) {
            insertProductBindingCode(pbc);
        }
    }

    /**
     * mongo+mysql
     * */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertProductBindingCode(ProductBindingCode pbc) {
        ProductBindingCodeQuery q = new ProductBindingCodeQuery();
        q.setProductName(pbc.getProductName());
        q.setIdCode(pbc.getIdCode());
        q.setCodeName(pbc.getCodeName());
        q.setLimit(1);
        FindIterable<Document> documents = collection.find(Filters.and(Filters.eq(MongoConstantName.productName,pbc.getProductName()),
                Filters.eq(MongoConstantName.idCode,pbc.getIdCode())));
        try{
            if(documents.first()==null){
                Document document=new Document();
                document.append(MongoConstantName.productName,pbc.getProductName());
                document.append(MongoConstantName.idCode,pbc.getIdCode());
                document.append(pbc.getCodeName(),pbc.getCode());
                document.append(MongoConstantName.createTime,new Date());
                document.append(MongoConstantName.updateTime,new Date());
                collection.insertOne(document);
            }else{
                Document search=new Document();
                search.append(MongoConstantName.productName,pbc.getProductName());
                search.append(MongoConstantName.idCode,pbc.getIdCode());
                Document update=new Document();
                update.append(pbc.getCodeName(),pbc.getCode());
                update.append(MongoConstantName.updateTime,new Date());
                Document updateSet=new Document();
                updateSet.append("$set",update);
                collection.updateOne(search,updateSet);
            }
            if (trDao.queryProductBindingCode(q).size()== 0) {
                trDao.insertProductBindingCode(pbc);
                trDao.setYesForUniformity(pbc);
            } else {
                trDao.updateProductBindingCode(pbc);
            }
            log.info("上传数据---->，提示信息idCode={},productName={},{}={}"
                    ,pbc.getIdCode()
                    ,pbc.getProductName()
                    ,pbc.getCodeName()
                    ,pbc.getCode());
        }catch (Exception e){
            log.error("请检查数据库是否不一致---->，提示信息idCode={},productName={},{}={}"
                    ,pbc.getIdCode()
                    ,pbc.getProductName()
                    ,pbc.getCodeName()
                    ,pbc.getCode());
            throw new RuntimeException();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void lostGroupBindingCode(ProductBindingCode pbc) {
        ProductBindingCodeQuery pbcq=new ProductBindingCodeQuery();
        pbcq.setIdCode(pbc.getIdCode());
        pbcq.setProductName(pbc.getProductName());
        List<ProductBindingCode> aProduct = trDao.queryProductBindingCode(pbcq);

        if(aProduct==null||aProduct.size()==0){
            log.error("无法定位到这个产品{}",pbc);
            throw new RequestParamException("数据库中无此产品");
        }
        //mysql数据库
        for (ProductBindingCode bindingCode : aProduct) {
            pbcq.setCodeName(bindingCode.getCodeName());
            trDao.deleteProductBindingCode(pbcq);
        }

        //Mongo数据库
        FindIterable<Document> documents =collection.find(Filters.eq(MongoConstantName.idCode, pbc.getIdCode()));
        Document target = documents.first();
        if(target==null){
            log.info("丢失一组属性时但是Mongo中没有，将保存到备份表中 ---> idCode={}",pbc.getIdCode());
            for (ProductBindingCode bindingCode : aProduct) {
                backupProduct(bindingCode);
            }
        }else{
            log.info("丢失一组属性时并保存到备份表中 ---> idCode={}",pbc.getIdCode());
            collection.deleteOne(target);
            backupColl.insertOne(target);
        }
    }

    /**
    * 在Mongo中删除实体时备份实体属性
    * */
    private void backupProduct(ProductBindingCode pbc){
        FindIterable<Document> documents = backupColl.find(Filters.and(Filters.eq(MongoConstantName.productName,pbc.getProductName()),
                Filters.eq(MongoConstantName.idCode,pbc.getIdCode())));
        try {
            if (documents.first() == null) {
                Document document = new Document();
                document.append(MongoConstantName.productName, pbc.getProductName());
                document.append(MongoConstantName.idCode, pbc.getIdCode());
                document.append(pbc.getCodeName(), pbc.getCode());
                document.append(MongoConstantName.createTime, new Date());
                document.append(MongoConstantName.updateTime, new Date());
                backupColl.insertOne(document);
            } else {
                Document search = new Document();
                search.append(MongoConstantName.productName, pbc.getProductName());
                search.append(MongoConstantName.idCode, pbc.getIdCode());
                Document update = new Document();
                update.append(pbc.getCodeName(), pbc.getCode());
                update.append(MongoConstantName.updateTime, new Date());
                Document updateSet = new Document();
                updateSet.append("$set", update);
                backupColl.updateOne(search, updateSet);
            }
        }catch (Exception e){
            log.error("备份数据时出错---->，提示信息idCode={},productName={},{}={}"
                    ,pbc.getIdCode()
                    ,pbc.getProductName()
                    ,pbc.getCodeName()
                    ,pbc.getCode());
            throw new RuntimeException();
        }
    }


    @Override
    public ProductBindingCode getProductBindingCode(String productName,
                                                    String codeName, String code) {
        ProductBindingCodeQuery q = new ProductBindingCodeQuery();
        q.setProductName(productName);
        q.setCode(code);
        q.setCodeName(codeName);
        q.setLimit(1);
        List<ProductBindingCode> pbcList = trDao.queryProductBindingCode(q);
        if(pbcList!=null&&pbcList.size()!=0){
            return pbcList.get(0);
        }
        return null;
    }

    @Override
    public List<String>  findAllIdCodeByCode(ProductBindingCode bindingCode) {
        if(bindingCode.getProductName()==null||bindingCode.getCodeName()==null||bindingCode.getCode()==null){
            log.error("传递的参数错误，{}",bindingCode);
            throw new RequestParamException("请求参数错误");
        }
        FindIterable<Document> idCodes = collection.find(
                Filters.and(
                        Filters.eq(MongoConstantName.productName, bindingCode.getProductName()),
                        Filters.eq(bindingCode.getCodeName(), bindingCode.getCode())))
                .projection(Projections.include(MongoConstantName.idCode));
        List<String> resultList=new ArrayList<>();
        for (Document idCode : idCodes) {
            resultList.add((String) idCode.get(MongoConstantName.idCode));
        }
        if(resultList.size()==0){
            return null;
        }else{
            return resultList;
        }
    }

    /**
    *
    * */
    @Override
    public List<String> getAllBindingCodeNameList(String productName) {
        return trDao.getAllBindingCodeNameList(productName);
    }

    @Override
    public List<ProductBindingCode> queryBindingCode(
            ProductBindingCodeQuery query) {
        return trDao.queryProductBindingCode(query);

    }

    /**
     * 获取所有属性通过productName和idCOde
     * */
    @Override
    public Document getBindingCodeByPI(String idCode){
        FindIterable<Document> documents = collection.find(Filters.eq(MongoConstantName.idCode, idCode));
        Document target = documents.first();
        Document repeat= documents.skip(1).first();
        if(target==null){
            log.error("没有idCode为<"+idCode+">的产品");
            throw new RuntimeException("没有idCode为<"+idCode+">的产品");
        }else if(repeat!=null){
            log.error("存在多个idCode为<"+idCode+">的产品");
            throw new RuntimeException("存在多个idCode为<"+idCode+">的产品");
        }else{
             target.remove(MongoConstantName.createTime);
             target.remove(MongoConstantName.updateTime);
             target.remove(MongoConstantName.default_id);
             return target;
        }
    }
}
